package paim.wingchun.app;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Transient;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.Hibernate;

import paim.wingchun.model.AField;
import paim.wingchun.model.AType;
import paim.wingchun.model.DAO.DAO;
import paim.wingchun.model.modelos.Model;
import paim.wingchun.model.pojos.Pojo;

public class Reflections {

    // FIXME colocar no POJO ou Modelo ou usar direto
    public static Class<?> getClasse(Object objeto) {
        return Hibernate.getClass(objeto);
    }

    /**
     * retorna a classe dos objetos da array baseado no primeiro elemento
     *
     * @author temujin Oct 10, 2011
     * @param <T>
     *            objetos
     * @return Class<T>
     */
    public static <T> Class<T> getClasse(List<T> objetos) throws IllegalArgumentException {
        if ( objetos.isEmpty() )
            new IllegalArgumentException();

        T primeiro = objetos.get(0);

        return (Class<T>) primeiro.getClass();
    }

    /**
     * busca {@link Field} de classe {@link Pojo}, incluindo superclasse se for {@link Pojo}
     *
     * @author temujin Jan 03, 2011
     * @param classePojo
     * @return List<Field>
     */
    public static List<Field> getDeepFields(Class<? extends Pojo> classePojo) {
        Class<?> classe = classePojo;
        List<Field> fields = new ArrayList<Field>();

        /* enquanto for uma subClasse de POJO, continua pegando fields */
        while ( Pojo.class.isAssignableFrom(classe) ) {
            for ( Field field : classe.getDeclaredFields() ) {
                /* introduzido pelo compilador nao */
                if ( field.isSynthetic() )
                    continue;

                /* esses nao me interessa */
                if ( field.getName().equals("serialVersionUID") )
                    continue;
                if ( field.getName().equals("id") )
                    continue;
                if ( field.getName().equals("versao") )
                    continue;
                if ( field.getName().equals("alterado") )
                    continue;
                if ( field.getName().equals("pendencia") )
                    continue;
                if ( field.getName().equals("erros") )
                    continue;

                // AField aField = field.getAnnotation(AField.class);

                /* transiente nao */
                if ( field.isAnnotationPresent(Transient.class) )
                    continue;

                /* listas nao */
                // if ( List.class.isAssignableFrom(field.getType()) )
                // continue;

                /* sem anotacao AField nï¿½o */
                // if ( aField == null )
                // continue;

                /* oculto nao */
                if ( field.isAnnotationPresent(AField.class) && field.getAnnotation(AField.class).isHidden() )
                    continue;

                /* o que sobrou eu quero */
                fields.add(field);
            }
            classe = classe.getSuperclass();
        }
        /* eu prefiro na ordem da classe mais ancestral primeiro */

        Collections.reverse(fields);
        return fields;
    }

    /**
     * buscar atributos do pojo, que nao sao lista e ... *
     *
     * @author temujin Jun 7, 2011
     * @param classeP
     * @return List<Field>
     */
    public static List<Field> getDeepSimpleFields(Class<? extends Pojo> classeP) {
        List<Field> fields = new ArrayList<Field>();

        for ( Field field : getDeepFields(classeP) ) {

            /* listas nao */
            if ( List.class.isAssignableFrom(field.getType()) )
                continue;

            /* o que sobrou eu quero */
            fields.add(field);
        }

        return fields;

    }

    public static Field getDeepField(Class<? extends Pojo> classeP, String fieldName) throws NoSuchFieldException {
        for ( Field field : getDeepFields(classeP) ) {
            if ( field.getName().equals(fieldName) )
                return field;
        }
        throw new NoSuchFieldException();
    }

    public static Field getDeepField(Object objeto, String fieldName) throws NoSuchFieldException {
        Class<? extends Pojo> classe = (Class<? extends Pojo>) getClasse(objeto);
        Field field = getDeepField(classe, fieldName);
        return field;
    }

    /**
     * @author temujin Sep 24, 2011
     * @param classeP
     * @return List<Field>
     */
    public static List<Field> getFields(Class<?> classeP) {
        List<Field> fields = new ArrayList<>();

        for ( Field field : classeP.getDeclaredFields() ) {

            /* introduzido pelo compilador nao */
            if ( field.isSynthetic() )
                continue;

            /* transiente nao */
            if ( field.isAnnotationPresent(Transient.class) )
                continue;

            /* sem anotacao AField nao */
            // if ( aField == null )
            // continue;

            /* oculto nao */
            if ( field.isAnnotationPresent(AField.class) && field.getAnnotation(AField.class).isHidden() )
                continue;

            /* listas nao */
            // if ( List.class.isAssignableFrom(field.getType()) )
            // continue;

            /* o que sobrou eu quero */
            fields.add(field);
        }

        return fields;
    }

    public static Field getField(Class<?> classeP, String fieldName) throws NoSuchFieldException {
        for ( Field field : getFields(classeP) ) {
            if ( List.class.isAssignableFrom(field.getType()) )

                if ( field.getName().equals(fieldName) )
                    return field;
        }
        throw new NoSuchFieldException();
    }

    /**
     * retorna o field do tipo fieldClass , sendo lista ou nao da classe casseP
     *
     * @author paim 26/09/2013
     * @since
     * @param classeP
     * @param fieldClass
     * @throws NoSuchFieldException
     * @return Field
     */
    public static Field getField(Class<?> classeP, Class<? extends Pojo> fieldClass) throws NoSuchFieldException {
        String fieldName = getFieldName(fieldClass);
        for ( Field field : getFields(classeP) ) {
            /* lista eh no plural */
            if ( field.getName().equals(fieldName)
                    || (List.class.isAssignableFrom(field.getType()) && field.getName().equals(fieldName + "s")) )
                return field;
        }

        throw new NoSuchFieldException();
    }

    public static Field getField(Object objeto, String fieldName) throws NoSuchFieldException {
        Class<? extends Pojo> classe = (Class<? extends Pojo>) getClasse(objeto);
        Field field = getField(classe, fieldName);
        return field;
    }

    public static String getFieldName(Class<? extends Pojo> classe) {
        String s = StringUtils.uncapitalize(classe.getSimpleName());
        return s;
    }

    /**
     * busca a classe do tipo do field<BR>
     * TODO implemente-me o resto
     *
     * @author temujin Oct 28, 2011
     * @param field
     * @return Class<?>
     * @throws Exception
     */
    public static Class<?> getTypeClass(Field field) {

        Type type = field.getGenericType();

        /* se o field for um tipado */
        if ( ParameterizedType.class.isAssignableFrom(type.getClass()) ) {
            // System.out.println("ParameterizedType");
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type primeiro = parameterizedType.getActualTypeArguments()[0];

            if ( WildcardType.class.isAssignableFrom(primeiro.getClass()) ) {
                /* eh uma mascara, acho que deve retornar o nome do atributo */
                // System.out.println("WildcardType");
                WildcardType wildcardType = (WildcardType) primeiro;
                wildcardType.getLowerBounds();// super
                wildcardType.getUpperBounds(); // extends

                try {
                    throw new Exception("TODO metodo nao pronto para WildcardType");
                }
                catch ( Exception e ) {
                    e.printStackTrace();
                }
                return List.class;
            }
            else {
                /* retornar a classe tipada */
                Class<?> classe = (Class<?>) parameterizedType.getActualTypeArguments()[0];
                return classe;
            }
        }
        else if ( GenericArrayType.class.isAssignableFrom(type.getClass()) ) {
            System.out.println("GenericArrayType");
            try {
                throw new Exception("TODO metodo nao pronto para GenericArrayType");
            }
            catch ( Exception e ) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }
        else if ( TypeVariable.class.isAssignableFrom(type.getClass()) ) {
            System.out.println("TypeVariable");
            try {
                throw new Exception("TODO metodo nao pronto para TypeVariable");
            }
            catch ( Exception e ) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }
        else {
            // System.out.println("Type");
            return field.getType();
        }
    }

    /**
     * valor do field no objeto
     *
     * @author temujin Sep 24, 2011
     * @param objeto
     * @param field
     * @return Object
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws NullPointerException
     * @throws NoSuchMethodException
     * @throws SecurityException
     */
    public static Object getValorField(Object objeto, Field field) throws IllegalArgumentException,
            IllegalAccessException, InvocationTargetException, NullPointerException, SecurityException,
            NoSuchMethodException {
        if ( objeto == null || field == null )
            throw new NullPointerException();

        Class<?> classe = getClasse(objeto);
        Method metodoGet = getMetodoGet(classe, field);
        Object value = metodoGet.invoke(objeto, (Object[]) null);
        return value;
    }

    public static Object getValorField(Object objeto, String fieldName) throws NoSuchFieldException,
            IllegalArgumentException, NullPointerException, IllegalAccessException, InvocationTargetException,
            SecurityException, NoSuchMethodException {
        return getValorField(objeto, getDeepField(objeto, fieldName));
    }

    // public static <T> List<T> getValorField(List<POJO> pojos, Class<T> fieldClass, Field field)
    // throws IllegalArgumentException, NullPointerException, NoSuchFieldException, IllegalAccessException,
    // InvocationTargetException {
    //
    // List<T> lista = new ArrayList<T>();
    // field.getDeclaringClass();
    // Field field = getField(classe, fieldName);
    // for ( POJO pojo : pojos ) {
    // Object value = Reflections.getValorField(pojo, fieldName);
    // lista.add((T) value);
    // }
    // return lista;
    // }

    public static <T> List<T> getValorField(List<? extends Pojo> pojos, Class<T> fieldClass, String fieldName)
            throws IllegalArgumentException, NullPointerException, NoSuchFieldException, IllegalAccessException,
            InvocationTargetException, SecurityException, NoSuchMethodException {

        if ( pojos.isEmpty() )
            return Collections.emptyList();

        List<T> lista = new ArrayList<T>();

        Class<? extends Pojo> classe = getClasse(pojos);

        Field field = getDeepField(classe, fieldName);
        // List.class.isAssignableFrom(field.getType())

        for ( Pojo pojo : pojos ) {
            Object value = getValorField(pojo, field);
            lista.add((T) value);
        }
        return lista;
    }

    public static Method getMetodoGet(Class<?> classe, Field field) throws SecurityException, NoSuchMethodException {
        String methodName = getMetodoGet(field);

        Method method = classe.getMethod(methodName, (Class[]) null);
        return method;

    }

    public static String getMetodoGet(Field field) {
        return "get" + StringUtils.capitalize(field.getName());
    }

    public static String getMetodoIs(Field field) {
        return "is" + StringUtils.capitalize(field.getName());
    }

    public static String getMetodoSet(Field field) {
        return "set" + StringUtils.capitalize(field.getName());
    }

    /* busca objeto modelo */
    public static Model<?> getModel(Class<? extends Pojo> classeP) throws Exception {
        try {
            Class<?> nomeClasseModelo = getModelClass(classeP);

            /* busca instancia do modelo */
            Method method = nomeClasseModelo.getMethod("getInstance", (Class[]) null);
            Object modelo = method.invoke(nomeClasseModelo);

            return (Model<?>) modelo;

        }
        catch ( Exception e ) {
            throw e;
        }
    }

    public static <P extends Pojo, M extends Model<P>> Class<M> getModelClass(Class<P> classePojo) {
        try {
            /* Se classe passado nao for tipo POJO */
            if ( !Pojo.class.isAssignableFrom(classePojo) )
                throw new ClassNotFoundException();

            if ( classePojo.getSimpleName().equals(Pojo.class.getSimpleName()) )
                return (Class<M>) Model.class;

            /* o pacote */
            String nomeClasseModelo = classePojo.getPackage().getName().replaceFirst(".pojos", ".modelos");

            nomeClasseModelo += ".";
            nomeClasseModelo += classePojo.getSimpleName() + "Model";

            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            Class<M> classeM = (Class<M>) loader.loadClass(nomeClasseModelo);

            return classeM;
        }
        catch ( ClassNotFoundException e ) {
            /* Se classe passado nao for tipo POJO ou nao for encontrada retorna Modelo */
            return (Class<M>) Model.class;
        }
    }

    public static <P extends Pojo, M extends Model<P>> DAO<P> getDAO(Class<M> classeModel) throws Exception {
        try {
            /* o pacote */
            String nomeClasseDAO = classeModel.getPackage().getName().replaceFirst(".modelos", ".DAO");
            nomeClasseDAO += ".";
            /* o simplename */
            nomeClasseDAO += classeModel.getSimpleName().replaceFirst("Model", "DAO");

            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            Class<?> classeDAO = loader.loadClass(nomeClasseDAO);
            Method getInstancia = classeDAO.getMethod("getInstance", (Class[]) null);
            /* Retorna classe de DAO correspondente (filha) */
            return (DAO<P>) getInstancia.invoke(classeDAO, (Object[]) null);
        }
        catch ( ClassNotFoundException e ) {
            /* Retorna classe de DAO ancestral */
            return (DAO<P>) DAO.getInstance();
        }
        catch ( Exception e ) {
            throw e;
        }
    }


    /**
     * retorna o {@link AType#rotulo()} do {@link Class} passado
     *
     * @author paim 25/10/2011
     * @param classe
     * @return String
     */
    public static String getRotulo(Class<?> classe) {
        AType aType = classe.getAnnotation(AType.class);

        if ( aType == null || aType.rotulo().isEmpty() )
            return classe.getSimpleName();
        return aType.rotulo();
    }

    /**
     * retorna o {@link AType#rotuloS()} do {@link Class} passado
     *
     * @author paim 25/10/2011
     * @param classe
     * @return String
     */
    public static String getRotuloS(Class<?> classe) {
        AType aType = classe.getAnnotation(AType.class);

        if ( aType == null || aType.rotuloS().isEmpty() )
            return classe.getSimpleName() + "list";
        return aType.rotuloS();
    }

    /**
     * retorna o {@link AField#rotulo()} do {@link Field} passado
     *
     * @author paim 25/10/2011
     * @param field
     * @return String
     */
    public static String getRotulo(Field field) {
        AField aField = field.getAnnotation(AField.class);

        /* se nao foi especificado, retorna o rotulo da classe do atributo */
        if ( aField == null || aField.rotulo().isEmpty() ) {
            Class<?> classeTipo = getTypeClass(field);
            return getRotuloS(classeTipo);
        }

        return aField.rotulo();
    }

    /**
     * retorna o {@link AField#rotulo()} do fieldname passado
     *
     * @author paim 25/10/2011
     * @param fieldname
     * @return String
     */
    public static String getRotulo(Class<? extends Pojo> classeP, String fieldname) {
        Field field;
        try {
            field = getDeepField(classeP, fieldname);
        }
        catch ( NoSuchFieldException | SecurityException e ) {
            return null;
        }

        return getRotulo(field);
    }

    /**
     * uma {@link List} de rotulos dos campos {@link Field} passados
     *
     * @author paim 25/10/2011
     * @param field
     * @return List<String>
     */
    public static List<String> getRotuloList(List<Field> field) {
        List<String> rotulos = new ArrayList<String>();
        for ( Field f : field ) {
            rotulos.add(getRotulo(f));
        }

        return rotulos;
    }

    /**
     * Get the underlying class for a type, or null if the type is a variable type.
     * http://www.artima.com/weblogs/viewpost.jsp?thread=208860
     *
     * @param type
     *            the type
     * @return the underlying class
     */
    public static Class<?> getClass(Type type) {
        if ( type instanceof Class ) {
            return (Class) type;
        }
        else if ( type instanceof ParameterizedType ) {
            return getClass(((ParameterizedType) type).getRawType());
        }
        else if ( type instanceof GenericArrayType ) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            Class<?> componentClass = getClass(componentType);
            if ( componentClass != null ) {
                return Array.newInstance(componentClass, 0).getClass();
            }
            else {
                return null;
            }
        }
        else {
            return null;
        }
    }

    /**
     * Get the actual type arguments a child class has used to extend a generic base class.
     * http://www.artima.com/weblogs/viewpost.jsp?thread=208860
     *
     * @param baseClass
     *            the base class
     * @param childClass
     *            the child class
     * @return a list of the raw classes for the actual type arguments.
     */
    public static <T> List<Class<?>> getTypeArguments(Class<T> baseClass, Class<? extends T> childClass) {
        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
        Type type = childClass;
        // start walking up the inheritance hierarchy until we hit baseClass
        while ( !getClass(type).equals(baseClass) ) {
            if ( type instanceof Class ) {
                // there is no useful information for us in raw types, so just keep going.
                type = ((Class) type).getGenericSuperclass();
            }
            else {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Class<?> rawType = (Class) parameterizedType.getRawType();

                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
                for ( int i = 0; i < actualTypeArguments.length; i++ ) {
                    resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
                }

                if ( !rawType.equals(baseClass) ) {
                    type = rawType.getGenericSuperclass();
                }
            }
        }

        // finally, for each actual type argument provided to baseClass, determine (if possible)
        // the raw class for that type argument.
        Type[] actualTypeArguments;
        if ( type instanceof Class ) {
            actualTypeArguments = ((Class) type).getTypeParameters();
        }
        else {
            actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
        }
        List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
        // resolve types by chasing down type variables.
        for ( Type baseType : actualTypeArguments ) {
            while ( resolvedTypes.containsKey(baseType) ) {
                baseType = resolvedTypes.get(baseType);
            }
            typeArgumentsAsClasses.add(getClass(baseType));
        }
        return typeArgumentsAsClasses;
    }

    /** retorna o primeiro agumento do tipo pojo encontrado ou null */
    @Deprecated
    public static <P extends Pojo> Class<P> getPojoParameter(final Class<?> clazz) {
        return getClassParameter(clazz, Pojo.class);
    }


    /**
     * retorna o primeiro parametro assignableFromClass da parameterizedClass
     *
     * @author temujin Mar 16, 2014
     * @param parameterizedClass
     * @param assignableFromClass
     * @return Class<X>
     */
    public static <X> Class<X> getClassParameter(final Class<?> parameterizedClass, Class<? super X> assignableFromClass) {

        // TODO porque nao pode ser interface? generaliza essa merda
        if ( parameterizedClass.isInterface() )
            throw new IllegalArgumentException();

        final Type type = parameterizedClass.getGenericSuperclass();

        // parameterizedClass.getDeclaringClass();
        // parameterizedClass.getEnclosingClass();
        // parameterizedClass.getGenericInterfaces();
        // parameterizedClass.getGenericSuperclass();
        //
        // parameterizedClass.getTypeParameters();

        /* se a classe nao eh parametrizada */
        if ( !ParameterizedType.class.isAssignableFrom(type.getClass()) || type == null )
            throw new MalformedParameterizedTypeException();

        ParameterizedType parameterizedType = (ParameterizedType) type;

        Type[] typeArguments = parameterizedType.getActualTypeArguments();
        for ( Type typeArgument : typeArguments ) {

            /*se for por exemplo um T ou um P...  sun.reflect.generics.reflectiveObjects.TypeVariableImpl*/
            if ( typeArgument instanceof TypeVariable || typeArgument instanceof WildcardType ) {
                // {// FIXME deveria buscar na classe ancestral entao
                // ((TypeVariable<?>) typeArgument).getBounds();
                // ((TypeVariable<?>) typeArgument).getClass();
                // ((TypeVariable<?>) typeArgument).getGenericDeclaration();
                // ((TypeVariable<?>) typeArgument).getTypeName();
                // ((TypeVariable<?>) typeArgument).getName();
                // ((TypeVariable<?>) typeArgument).getTypeName();
                // }
                continue;
            }
            /*se o parametro for um tipo parametrizado entao so me interessa o parametro em si*/
            if (typeArgument instanceof ParameterizedType){
                ParameterizedType  parameterizedType2 = (ParameterizedType)typeArgument;
                Type type2 = parameterizedType2.getRawType();
                if (type2 instanceof TypeVariable || type2 instanceof WildcardType)
                    continue;

                if ( type2 instanceof Class ){
                    if ( assignableFromClass.isAssignableFrom( (Class<?>) type2) )
                        return (Class<X>) type2;
                }
            }

            if ( typeArgument instanceof Class ){
                if ( assignableFromClass.isAssignableFrom((Class<?>) typeArgument) )
                    return (Class<X>) typeArgument;
            }
        }

        /* tenta na superclasse */
        return getClassParameter(parameterizedClass.getSuperclass(), assignableFromClass);
    }


    @Deprecated
    public static Class<?> getInterfaceParameter(final Class<?> parameterizedClass,Class<? > assignableFromClass) {
            throw new MalformedParametersException();
    }

    public static Class<?> getInterfaceGenericTypeArgument(final Class<?> parameterizedClass,
            Class<?> assignableFromClass) {

        Type[] types = parameterizedClass.getGenericInterfaces();
        if ( types.length == 0 )
            throw new MalformedParameterizedTypeException();

        for ( Type type : types ) {
            if ( !ParameterizedType.class.isAssignableFrom(type.getClass()) )
                /* se a classe nao eh parametrizada */
                continue;
        }

        final Type type = parameterizedClass.getGenericSuperclass();

        ParameterizedType parameterizedType = (ParameterizedType) type;

        Type[] typeArguments = parameterizedType.getActualTypeArguments();
        for ( Type typeArgument : typeArguments ) {
            Class classe = (Class) typeArgument;
            if ( assignableFromClass.isAssignableFrom(classe) ) {
                return classe;
            }
        }

        return getClassParameter(parameterizedClass.getSuperclass(), assignableFromClass);
    }

    @SuppressWarnings("unchecked")
    public static <T extends Pojo> Class<T> getGenericTypeArgument(final Class<?> clazz) {
        final Type type = clazz.getGenericSuperclass();
        Class<? extends Type> olka = type.getClass();
        if ( ParameterizedType.class.isAssignableFrom(type.getClass()) ) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type primeiro = parameterizedType.getActualTypeArguments()[0];
            Type[] xila = parameterizedType.getActualTypeArguments();
            System.out.println("lkjlkjk");
        }

        TypeVariable<?>[] aaa = clazz.getTypeParameters();
        Type[] oka = aaa[0].getBounds();

        ParameterizedType paramType;
        try {
            paramType = (ParameterizedType) type;
        }
        catch ( ClassCastException cause ) {
            paramType = (ParameterizedType) ((Class<T>) type).getGenericSuperclass();
        }
        return (Class<T>) paramType.getActualTypeArguments()[0];

        // if ( ParameterizedType.class.isAssignableFrom(type.getClass()) ) {
        // // System.out.println("ParameterizedType");
        // ParameterizedType parameterizedType = (ParameterizedType) type;
        // Type primeiro = parameterizedType.getActualTypeArguments()[0];
        //
        // /* retornar a classe tipada */
        // Class<?> classe = (Class<?>) parameterizedType.getActualTypeArguments()[0];
        // return classe;
        // }
        // }
    }

    // ##################################################################################

    @SuppressWarnings("unchecked")
    public static <T> Class<T> getGenericTypeArgument(final Class<?> clazz, final int idx) {
        final Type type = clazz.getGenericSuperclass();

        ParameterizedType paramType;
        try {
            paramType = (ParameterizedType) type;
        }
        catch ( ClassCastException cause ) {
            paramType = (ParameterizedType) ((Class<T>) type).getGenericSuperclass();
        }

        return (Class<T>) paramType.getActualTypeArguments()[idx];
    }

    public static <T> Class<T> getGenericTypeArgument(final Member member, final int idx) {
        Class<T> result = null;

        if ( member instanceof Field ) {
            result = getGenericTypeArgument((Field) member, idx);
        }
        else if ( member instanceof Method ) {
            result = getGenericTypeArgument((Method) member, idx);
        }

        return result;
    }

    @SuppressWarnings("unchecked")
    public static <T> Class<T> getGenericTypeArgument(final Field field, final int idx) {
        final Type type = field.getGenericType();
        final ParameterizedType paramType = (ParameterizedType) type;

        return (Class<T>) paramType.getActualTypeArguments()[idx];
    }

    @SuppressWarnings("unchecked")
    public static <T> Class<T> getGenericTypeArgument(final Method method, final int pos) {
        return (Class<T>) method.getGenericParameterTypes()[pos];
    }

    public static Object getFieldValue(Field field, Object object) {
        Object result = null;

        try {
            boolean acessible = field.isAccessible();
            field.setAccessible(true);
            result = field.get(object);
            field.setAccessible(acessible);

        }
        catch ( Exception e ) {

        }

        return result;
    }

    public static void setFieldValue(Field field, Object object, Object value) {
        try {
            boolean acessible = field.isAccessible();
            field.setAccessible(true);
            field.set(object, value);
            field.setAccessible(acessible);

        }
        catch ( Exception e ) {

        }
    }

    public static Field[] getNonStaticDeclaredFields(Class<?> type) {
        List<Field> fields = new ArrayList<Field>();

        if ( type != null ) {
            for ( Field field : type.getDeclaredFields() ) {
                if ( !Modifier.isStatic(field.getModifiers()) && !field.getType().equals(type.getDeclaringClass()) ) {
                    fields.add(field);
                }
            }
        }

        return fields.toArray(new Field[0]);
    }

    // ##################################################################################

}
